const fs = require("fs");
const path = require("path");

// 获取指定目录下所有文件夹
async function getFloderList(pathname, callback = () => true) {
  const state = await fs.promises.stat(pathname);
  const isDirectory = state.isDirectory();
  if (!isDirectory) return [];
  const fileList = await fs.promises.readdir(pathname);

  const directoryList = await (async () => {
    const arr = [];
    for (const filename of fileList) {
      const curFilePath = path.resolve(pathname, filename);
      const fileState = await fs.promises.stat(curFilePath);
      if (fileState.isDirectory()) {
        arr.push([filename, fileState]);
      }
    }
    return arr;
  })();

  return directoryList.filter(([dirname, state]) => callback(dirname, state));
}

// 获取指定目录下所有文件
async function getFileList(pathname, callback = () => true) {
  const state = await fs.promises.stat(pathname);
  const isDirectory = state.isDirectory();
  if (!isDirectory) return [];
  const fileList = await fs.promises.readdir(pathname);

  const newFileList = await (async () => {
    const arr = [];
    for (const filename of fileList) {
      const curFilePath = path.resolve(pathname, filename);
      const fileState = await fs.promises.stat(curFilePath);
      if (fileState.isFile()) {
        arr.push([filename, fileState]);
      }
    }
    return arr;
  })();

  return newFileList.filter(([filename, state]) => callback(filename, state));
}

// 处理正则特殊字符
function escapeRegex(reg) {
  reg = reg
    .replace(/\\/g, "\\\\") // 需要放在最前面，以免其它反斜杠混淆
    .replace(/\./g, "\\.")
    .replace(/\^/g, "\\^")
    .replace(/\$/g, "\\$")
    .replace(/\*/g, "\\*")
    .replace(/\+/g, "\\+")
    .replace(/\?/g, "\\?")
    .replace(/\(/g, "\\(")
    .replace(/\)/g, "\\)")
    .replace(/\[/g, "\\[")
    .replace(/\]/g, "\\]")
    .replace(/\{/g, "\\{")
    .replace(/\}/g, "\\}")
    .replace(/\|/g, "\\|")
    .replace(/\//g, "\\/");
  return reg;
}

// 获取指定目录下所有md文件用上的图片资源列表
async function getMdUseImgs(pathname, assetsDir = "assets") {
  const reg = new RegExp(
    `${escapeRegex(assetsDir)}\/.*?\.(png|jpg|jpeg|gif|bmp|svg|webp)`,
    "g"
  );
  const state = await fs.promises.stat(pathname);
  if (!state.isDirectory()) return [];
  const mdList = await getFileList(pathname, (filename) =>
    filename.endsWith(".md")
  );
  const len = mdList.length;
  const result = [];
  // 循环读取每一个md文件，找出里面的图片资源
  for (let i = 0; i < len; i++) {
    const [filename, state] = mdList[i];
    // 如果是软链接，则跳过
    if (state.isSymbolicLink()) continue;

    const content = await fs.promises.readFile(
      path.resolve(pathname, filename),
      "utf-8"
    );
    const imgList = content.match(reg);
    imgList?.length &&
      result.push([filename, imgList.map((img) => img.replace("assets/", ""))]);
  }

  return result;
}

// 递归查找未使用资源
async function core(pathname, assetsDir = "assets", totalResult = []) {
  const assetsPath = path.resolve(pathname, assetsDir);
  const isHas = fs.existsSync(assetsPath);

  if (isHas) {
    // 存在指定文件夹
    // 获取这个资源文件夹中所有文件
    const fileList = await getFileList(assetsPath);
    // 获取上级文件夹中所有md使用到的图片资源
    const imgList = await getMdUseImgs(pathname);
    const flatImgList = new Set(imgList.flatMap(([_, imgList]) => imgList));
    // console.log(fileList.length, flatImgList.size);
    // 通知用户，哪个目录下有哪些资源没被使用
    const result = fileList
      .filter(([filename]) => !flatImgList.has(filename))
      .map(([filename]) => filename);

    result.length && totalResult.push([assetsPath, result]);
  }

  // 判断是否还有子文件夹，若有则递归
  const directoryList = await getFloderList(pathname);
  for (const [dirname, state] of directoryList) {
    await core(path.resolve(pathname, dirname), assetsDir, totalResult);
  }

  return totalResult;
}

(async () => {
  const unUseImgList = await core(__dirname);
  const len = unUseImgList.flatMap(([_, assets]) => assets).length;
  if (len === 0) return console.log(`操作执行完毕，没有未使用的图片资源`);

  for (let i = 0; i < unUseImgList.length; i++) {
    const [filename, assets] = unUseImgList[i];
    for (let j = 0; j < assets.length; j++) {
      const pathname = path.resolve(filename, assets[j]);
      try {
        await fs.promises.unlink(pathname);
      } catch (error) {
        console.log("文件删除失败：", error);
      }
    }
  }

  console.log(`操作执行完毕，共删除：${len}个未使用图片资源`);
})();
